//Design a class (or a class library) to handle unlimited integer operations (integer larger )
//Provide the class standard overloaded arithmetic operators.
//
//follow the rules "do as ints do".
//
//Write a test for BigInt operations:
//
//Example:
//
//{
//    BigInt x = 569; // ctor with an integer
//    BigInt y; // default ctor
//    y = pow(x, 10); // 569*569*...*569 ten times (a big number!)
//    BigInt z = x; //copy ctor
//    z = x; // operator=
//    z += y; // operator+, operator+=, -, -=, *, *=
//    z %= y; // operator%, operator/ operator%= 
//    z %=  5; // same operators of above... it is better to provide overloads for int or just convert int to BigInt?
//    BigInt w("348734834853836786386736"); // yeah, ctor that takes a string!
//    w++; // postincrement, preincrement
//    if(w <= y) // yeeeah, comparison! <, <=, >, >=, ==, !=
//        z &= x; // bitwise and, or, xor...
//    std:: cout << y; // obviously streams operators...
//    x = -y; // yeah, signed/unsigned, unary operator -
//    x <<= 5; // yeah, shift operators!
//    w = pow(w,y); // ok, this will crack your ram, but you have to implement pow for both base and exponent BigInt
//}
//
//do not google for it! (I'll show you next lesson my implementation)
//
//suggestions and hints:
//- choose the right drawback for: time efficiency, space efficiency, digit access
//- reuse code: do not write the same stuff more than once! (how will you implement operator!= if you have operator== ?)
//- document the code. provide a clear class interface
//- write an extensive test suite (I've developed my solution using gtest, the google testing framework, and developing in TDD philosophy)
//- enjoy it!

#pragma once

#include <string>
#include <vector>
class BigInteger{

public:
	BigInteger();
	BigInteger(int );
	BigInteger(unsigned int);
	BigInteger(const BigInteger& that);
	BigInteger(const std::string& s);

	~BigInteger();

	/*----- OPERATORS -------*/
	BigInteger& operator =(const BigInteger& that);
	bool operator ==(const BigInteger& that);

	BigInteger& operator <<= (const int i_shiftAmount);
	BigInteger& operator >>= (const int i_shiftAmount);
	BigInteger& operator += (const BigInteger& that);
	BigInteger& operator -= (const BigInteger& that);
	BigInteger& operator *= (const BigInteger& that);
	BigInteger& operator /= (const BigInteger& that);
	BigInteger& operator %= (const BigInteger& that);

	BigInteger operator ~();

	BigInteger& operator ++(); //pre-increment
	BigInteger operator ++(int );
	BigInteger& operator --();
	BigInteger operator --(int );


	friend BigInteger operator -(const BigInteger& first,const BigInteger& second );
	friend bool operator <(const BigInteger & a, const BigInteger& b);
	friend bool operator <=(const BigInteger & a,const BigInteger& b);
	friend bool operator >(const BigInteger & a, const BigInteger& b);
	friend bool operator >=(const BigInteger & a,const BigInteger& b);
	friend bool operator ==(const BigInteger & a,const BigInteger& b);
	friend bool operator !=(const BigInteger & a,const BigInteger& b);


	// ------------------ BITWISE ----------------------------
	friend BigInteger operator ~(const BigInteger& a);
	friend BigInteger operator &(const BigInteger& a, const BigInteger& b);
	friend BigInteger operator |(const BigInteger& a, const BigInteger& b);
	friend BigInteger operator ^(const BigInteger& a, const BigInteger& b);

	friend BigInteger EthiopianMultiplication (const BigInteger& a, const BigInteger& b);
	//------------ OTHER METHODS---------------

	static BigInteger pow(const BigInteger& base, const BigInteger& exp) ;

	inline const bool isPositive() const;
	const bool IsEven() const;
	const bool IsOdd() const;
	void PrintBinary() const;

private:

	static const unsigned short defaultSliceNum = 4;
	static const unsigned int sliceSize = sizeof(unsigned int);
public:
	unsigned short MostSignificantSlice;
	std::vector<unsigned int> buffer;

	void initBuffer(unsigned int val); //fill the buffer with the given value
	void init(unsigned int sliceNum);
	void reserveMoreSpace(int moreSpace);
	unsigned int MostSignificantBitIndex() const; 

	std::string asString() const;
	void complement(); //two's complement

	
};

BigInteger operator <<(const BigInteger& a, const int i_shiftAmount);
BigInteger operator >>(const BigInteger& a, const int i_shiftAmount);

BigInteger operator +(const BigInteger& first,const BigInteger& second );
BigInteger operator /(const BigInteger& first,const BigInteger& second );
BigInteger operator -(const BigInteger& a);
BigInteger operator %(const BigInteger& a, const BigInteger& b);
BigInteger operator *(const BigInteger& first,const BigInteger& second );

std::ostream& operator<<(std::ostream& out, const BigInteger& i_integer);
//BigInteger operator <<(const BigInteger& a, const int i_shiftAmount);